﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;

/// <summary>
/// This class containing all methods and enums used for bug management
/// </summary>
public class BugManagement
{
    /// <summary>
    /// current logged in user
    /// </summary>
    User currentUser;

    /// <summary>
    /// current working bug
    /// </summary>
    Bug currentBug;

    /// <summary>
    /// pass the current logged in user to this instance
    /// </summary>
    /// <param name="currentUser">current logged in user</param>
    public BugManagement(User currentUser)
    {
        this.currentUser = currentUser;
        this.currentBug = null;
    }

    /// <summary>
    /// pass the current logged in user and current working bug to this instance
    /// </summary>
    /// <param name="currentUser">current logged in user</param>
    /// <param name="currentBug">current working bug</param>
    public BugManagement(User currentUser, Bug currentBug)
    {
        this.currentUser = currentUser;
        this.currentBug = currentBug;
    }

    /// <summary>
    /// Bug Type
    /// </summary>
    public enum Type
    {
        Bug, // bug
        Enhancement, // suggestion or improvement to project
        [Description("New Feature")]
        NewFeature, // new feature to implement
        Task, // a task, eg: finish documentation
        Question // not sure if it's a bug
    }

    /// <summary>
    /// Possible statuses of a bug
    /// </summary>
    public enum Status
    {
        [Description("To be confirmed")]
        ToBeConfirmed, // newly created bug, not confirmed yet
        Confirmed, // confirmed bug, not assigned to anyone
        [Description("Fixed (to be confirmed)")]
        FixedToBeConfirmed, // programmer set
        Closed, // closed bug
        [Description("Re-opened")]
        Reopened // bug is re-opened because it happens again after reported fixed
    }

    /// <summary>
    /// Possible statuses of a bug, short names
    /// </summary>
    public enum ShortStatus
    {
        New, // newly created bug, not confirmed yet
        Confirmed, // confirmed bug, not assigned to anyone
        Fixed, // programmer set
        Closed, // closed bug
        Reopened // bug is re-opened because it happens again after reported fixed
    }

    /// <summary>
    /// Bug Severity
    /// </summary>
    public enum Severity
    {
        Enhancement, // Request for enhancement
        Trivial, // Cosmetic problem like misspelled words or misaligned text
        Minor, // Minor loss of function, or other problem where easy workaround is present
        Major, // Major loss of function
        Critical, // Crashes, loss of data, severe memory leak
        Catastrophic // Blocks development and/or testing work
    }

    /// <summary>
    /// Bug priority
    /// </summary>
    public enum Priority
    {
        Low, // may fix later
        Medium, // fix when you can
        High // fix immediately
    }

    /// <summary>
    /// Reproducability of bug
    /// </summary>
    public enum Reproducability
    {
        Easy, // it's easy to reproduce bug
        Hard, // it's hard to reproduce bug but I know how to
        Random, // this bug happens randomly
        [Description("Not Sure")]
        NotSure, // I'm not sure how to reproduce this bug
        [Description("Cannot Reproduce")]
        CannotReproduce, // I cannot seem to reprodcue this
    }

    /// <summary>
    /// How a bug resolution was determined
    /// </summary>
    public enum Resolution
    {
        [Description("Not worth fixing")]
        NotWorthFixing, // will not fix this bug because it is trivial
        [Description("Cannot fix")]
        CannotFix, // will not fix this bug because it is an out-of-reach bug
        [Description("Not have to fix")]
        NotHaveToFix, // will not fix, other reason, eg: feature where bug orcurs is halted
        [Description("Not a bug")]
        NotABug, // this is not a bug to fix
        Duplicate, // this bug was reported before
        [Description("Cannot Reproduce")]
        CannotReproduce, // this bug cannot be reproduced
        [Description("Fix Successfully")]
        FixSuccessfully // bug was fixed successfully
    }

    /// <summary>
    /// Create a new bug and add to system
    /// </summary>
    /// <param name="projectId">project ID of the project where bug belongs to</param>
    /// <param name="type">Type of bug, get from enum Type</param>
    /// <param name="summary">bug short summary (title)</param>
    /// <param name="reproducability">reproducability of bug, get from enum Reproducability</param>
    /// <param name="reproductionSteps">instruction on how to reproduce bug</param>
    /// <param name="bugVersion">application version where bug was found</param>
    /// <param name="bugComponent">application component where bug was found</param>
    /// <param name="severity">bug severity, get from enum Severity</param>
    /// <param name="priority">priority of bug, get from enum Priority</param>
    /// <returns>new Bug ID if new bug added succeeds, otherwise -1 with exeption thrown</returns>
    public int AddBug(int projectId, int type, string summary, int reproducability,
        string reproductionSteps, string bugVersion, string bugComponent, int severity, int priority, string description)
    {
        try
        {
            // create a new bug
            // need not define bugID as it is automatically added by database
            Bug newBug = new Bug();

            // project where bug belongs to
            newBug.ProjectId = projectId;

            // set required fields
            newBug.Reproducability = reproducability;
            newBug.ReproductionSteps = reproductionSteps;
            newBug.BugFoundInVersion = bugVersion;
            newBug.BugFoundInComponent = bugComponent;
            newBug.Summary = summary;
            newBug.ReportedUser = newBug.LastUpdatedUser = currentUser.Username;
            newBug.ReportedDate = newBug.LastUpdatedDate = DateTime.Now;

            // NOTE: set int value from appropriate enums
            newBug.Type = type;
            newBug.Severity = severity;
            newBug.Priority = priority;
            // default status set to "ToBeConfirmed"
            int intstatus = (int)Status.ToBeConfirmed;
            newBug.Status = intstatus;
            // no resolution yet
            newBug.Description = description;
            // add new bug to database
            ApplicationGlobalVariables.KillDaBugsDatabase.Bugs.InsertOnSubmit(newBug);
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // log this event of adding bug
            Logging.AddLog(Logging.LogEvent.AddBug, newBug, currentUser.Username, null);

            // if everything alright
            return newBug.BugId;
        }
        catch (Exception ex)
        {
            return -1;
            throw ex;
        }
    }

    /// <summary>
    /// Update bug's basic info, to close a bug, use SetBugStatus
    /// </summary>
    /// <param name="bugId">bug ID of bug to update</param>
    /// <param name="type">new type of bug</param>
    /// <param name="summary">new summary</param>
    /// <param name="description">new description</param>
    /// <param name="bugVersion">version of application that bug is found</param>
    /// <param name="bugComponent">component of application that bug is found</param>
    /// <param name="severity">new severity of bug</param>
    /// <param name="reproducability">new bug reproducability</param>
    /// <param name="reproductionSteps">instructions for reproduction</param>
    /// <param name="priority">new bug priority</param>
    /// <returns>true if bug updated successfully, otherwisr false with exception thrown</returns>
    public bool UpdateBugInfo(int bugId, int type, string summary, string description, string bugVersion,
        string bugComponent, int severity, int reproducability, string reproductionSteps, int priority)
    {
        try
        {
            // get bug with bug ID
            Bug edittedBug = GetBug(bugId);

            // update fields with new values
            edittedBug.Type = type;
            edittedBug.Summary = summary;
            edittedBug.Reproducability = reproducability;
            edittedBug.BugFoundInVersion = bugVersion;
            edittedBug.BugFoundInComponent = bugComponent;
            edittedBug.Severity = severity;
            edittedBug.Priority = priority;
            edittedBug.Description = description;
            edittedBug.ReproductionSteps = reproductionSteps;

            // update hidden fields
            edittedBug.LastUpdatedUser = currentUser.Username;
            edittedBug.LastUpdatedDate = DateTime.Now;

            // update to database
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // log this event
            Logging.AddLog(Logging.LogEvent.UpdateBugInfo, edittedBug, currentUser.Username, null);

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// Update current bug's basic info, to close a bug, use SetBugStatus
    /// </summary>
    /// <param name="type">new type of bug</param>
    /// <param name="summary">new summary</param>
    /// <param name="description">new description</param>
    /// <param name="bugVersion">version of application that bug is found</param>
    /// <param name="bugComponent">component of application that bug is found</param>
    /// <param name="severity">new severity of bug</param>
    /// <param name="reproducability">new bug reproducability</param>
    /// <param name="reproductionSteps">instructions for reproduction</param>
    /// <param name="priority">new bug priority</param>
    /// <returns>true if bug updated successfully, otherwisr false with exception thrown</returns>
    public bool UpdateBugInfo(int type, string summary, string description, string bugVersion,
        string bugComponent, int severity, int reproducability, string reproductionSteps, int priority)
    {
        try
        {
            Bug edittedBug = currentBug;

            // update fields with new values
            edittedBug.Type = type;
            edittedBug.Summary = summary;
            edittedBug.Reproducability = reproducability;
            edittedBug.BugFoundInVersion = bugVersion;
            edittedBug.BugFoundInComponent = bugComponent;
            edittedBug.Severity = severity;
            edittedBug.Priority = priority;
            edittedBug.Description = description;
            edittedBug.ReproductionSteps = reproductionSteps;

            // update hidden fields
            edittedBug.LastUpdatedUser = currentUser.Username;
            edittedBug.LastUpdatedDate = DateTime.Now;

            // update to database
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // log this event
            Logging.AddLog(Logging.LogEvent.UpdateBugInfo, edittedBug, currentUser.Username, null);

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// Change a bug's status
    /// </summary>
    /// <param name="bugId">ID of the bug to change status</param>
    /// <param name="status">new status</param>
    /// <returns>true if bug updated successfully, otherwise false</returns>
    public bool SetBugStatus(int bugId, int status)
    {
        try
        {
            // get bug with bug ID
            Bug edittedBug = GetBug(bugId);

            // update status
            edittedBug.Status = status;

            // update hidden fields
            edittedBug.LastUpdatedUser = currentUser.Username;
            edittedBug.LastUpdatedDate = DateTime.Now;

            // update to database
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // log this event
            Logging.AddLog(Logging.LogEvent.SetBugStatus, edittedBug, currentUser.Username, null);

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// Change current bug's status
    /// </summary>
    /// <param name="status">new status</param>
    /// <returns>true if bug updated successfully, otherwise false</returns>
    public bool SetBugStatus(int status)
    {
        try
        {
            Bug edittedBug = currentBug;

            // update status
            edittedBug.Status = status;

            // update hidden fields
            edittedBug.LastUpdatedUser = currentUser.Username;
            edittedBug.LastUpdatedDate = DateTime.Now;

            // update to database
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // log this event
            Logging.AddLog(Logging.LogEvent.SetBugStatus, edittedBug, currentUser.Username, null);

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// delete an existing bug, based on bug ID
    /// </summary>
    /// <param name="bugname">bug ID of the bug to be deleted</param>
    /// <returns>true if new bug deleted succeeds, otherwise false with exeption thrown</returns>
    public bool DeleteBug(int bugId)
    {
        try
        {
            // remove this bug from Bug table
            Bug deletedBug = GetBug(bugId);
            ApplicationGlobalVariables.KillDaBugsDatabase.Bugs.DeleteOnSubmit(deletedBug);
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // remove any log related to this bug

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// delete current bug, together with its logs
    /// </summary>
    /// <returns>true if bug deletion succeeds, otherwise false with exeption thrown</returns>
    public bool DeleteBug()
    {
        try
        {
            // remove this bug from Bug table
            ApplicationGlobalVariables.KillDaBugsDatabase.Bugs.DeleteOnSubmit(currentBug);
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // remove any log related to this bug
            Logging.ClearLogBug(currentBug.BugId);

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// assign a bug to user
    /// </summary>
    /// <param name="toUsername">username to assign bug to</param>
    /// <param name="bugId">bug Id of the bug to assign</param>
    /// <returns>true if assignment succeeds, otherwise false with exeption thrown</returns>
    public bool AssignUserToBug(string toUsername, int bugId)
    {
        try
        {
            User_Bug newUserToBugAssignment = new User_Bug();
            newUserToBugAssignment.Username = toUsername;
            newUserToBugAssignment.BugId = bugId;
            ApplicationGlobalVariables.KillDaBugsDatabase.User_Bugs.InsertOnSubmit(newUserToBugAssignment);
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // log this event
            Logging.AddLog(Logging.LogEvent.AssignUserToBug, currentBug, currentUser.Username, toUsername);

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// assign current bug to user
    /// </summary>
    /// <param name="toUsername">username to assign bug to</param>
    /// <returns>true if assignment succeeds, otherwise false with exeption thrown</returns>
    public bool AssignUserToBug(string toUsername)
    {
        try
        {
            User_Bug newUserToBugAssignment = new User_Bug();
            newUserToBugAssignment.Username = toUsername;
            newUserToBugAssignment.BugId = currentBug.BugId;
            ApplicationGlobalVariables.KillDaBugsDatabase.User_Bugs.InsertOnSubmit(newUserToBugAssignment);
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // log this event
            Logging.AddLog(Logging.LogEvent.AssignUserToBug, currentBug, currentUser.Username, toUsername);

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// revoke access of user from a bug
    /// </summary>
    /// <param name="toUsername">username to remove access</param>
    /// <param name="bugId">ID of the bug to remove user from</param>
    /// <returns>true if unassignment succeeds, otherwise false</returns>
    public bool UnassignUserFromBug(string toUsername, int bugId)
    {
        try
        {
            User_Bug userToBugUnassignment = ApplicationGlobalVariables.KillDaBugsDatabase.User_Bugs.Single(i => i.Username == toUsername && i.BugId == bugId);
            ApplicationGlobalVariables.KillDaBugsDatabase.User_Bugs.DeleteOnSubmit(userToBugUnassignment);
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // log this event
            Logging.AddLog(Logging.LogEvent.UnassignUserFromProject, currentBug, currentUser.Username, toUsername);

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// revoke access of user from current bug
    /// </summary>
    /// <param name="toUsername">username to remove access</param>
    /// <param name="bugId">ID of the bug to remove user from</param>
    /// <returns>true if unassignment succeeds, otherwise false</returns>
    public bool UnassignUserFromBug(string toUsername)
    {
        try
        {
            User_Bug userToBugUnassignment = ApplicationGlobalVariables.KillDaBugsDatabase.User_Bugs.Single(i => i.Username == toUsername && i.BugId == currentBug.BugId);
            ApplicationGlobalVariables.KillDaBugsDatabase.User_Bugs.DeleteOnSubmit(userToBugUnassignment);
            ApplicationGlobalVariables.KillDaBugsDatabase.SubmitChanges();

            // log this event
            Logging.AddLog(Logging.LogEvent.UnassignUserFromProject, currentBug, currentUser.Username, toUsername);

            // if everything alright
            return true;
        }
        catch (Exception ex)
        {
            return false;
            throw ex;
        }
    }

    /// <summary>
    /// returns a list of Bugs by the Project it belongs to
    /// </summary>
    /// <param name="projectId">project ID of bug's parent project</param>
    /// <returns>list of Bugs by the Project it belongs to</returns>
    public List<Bug> ViewBug(int projectId)
    {
        try
        {
            var result = ApplicationGlobalVariables.KillDaBugsDatabase.Bugs.Where(i => i.ProjectId == projectId);
            return result.ToList();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// returns a list of Bugs a user is assigned to
    /// </summary>
    /// <param name="username">username of the user</param>
    /// <returns>list of Bugs the user is assigned to</returns>
    public List<Bug> ViewBug()
    {
        try
        {
            return (List<Bug>)ApplicationGlobalVariables.KillDaBugsDatabase.User_Bugs.Where(i => i.Username == currentUser.Username).Select(i => i.Bug);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// get Bug object of a bug ID
    /// </summary>
    /// <param name="bugId">bug ID to get Bug object</param>
    /// <returns>Bug object</returns>
    public static Bug GetBug(int bugId)
    {
        try
        {
            return ApplicationGlobalVariables.KillDaBugsDatabase.Bugs.Single(i => i.BugId == bugId);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}